package com.tendory.rxlocation;

import android.content.Context;

import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationListener;

import java.text.SimpleDateFormat;
import java.util.Date;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.Observer;
import io.reactivex.Scheduler;
import io.reactivex.Single;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;


/**
 * 高德RxLocationManager的实现类.
 * Created by pitt on 2017/3/16.
 */

final class AMapRxLocationManager implements RxLocationManager {
    //    private static volatile AMapRxLocationManager sINSTANCE;
    private volatile AMapLocationClient mAMapLocationClient;
    private ClientOption clientOption;

    private AMapRxLocationManager(final Context context) {
        mAMapLocationClient = new AMapLocationClient(context.getApplicationContext());
    }

    @SuppressWarnings("PMD.NonThreadSafeSingleton")
    static AMapRxLocationManager create(final Context context) {
        AMapRxLocationManager sINSTANCE = null;
        if (sINSTANCE == null) {
            synchronized (AMapRxLocationManager.class) {
                if (sINSTANCE == null) {
                    sINSTANCE = new AMapRxLocationManager(context);
                }
            }
        }
        return sINSTANCE;
    }

    @Override
    public Observable<MyLocation> getLastLocation() {
        return Single.just(mAMapLocationClient.getLastKnownLocation()).toObservable().compose(new Tras());
    }

    @Override
    public Observable<MyLocation> requestLocation() {
        final RxLocationListener rxLocationListener = new RxLocationListener(mAMapLocationClient);
        return Observable.unsafeCreate(rxLocationListener).compose(new Tras());
    }

    private class Tras implements ObservableTransformer<AMapLocation, MyLocation> {
        @Override
        public ObservableSource<MyLocation> apply(Observable<AMapLocation> upstream) {
            return upstream.map(new Function<AMapLocation, MyLocation>() {
                @Override
                public MyLocation apply(AMapLocation aMapLocation) throws Exception {
                    if (clientOption.isHandleError()) {
                        if (aMapLocation == null) {
                            throw new MyLocationException("loc null");
                        }
                        int err = aMapLocation.getErrorCode();
                        if (err != 0) {
                            throw new MyLocationException("location Error, ErrCode:"
                                    + aMapLocation.getErrorCode() + ", errInfo:" + aMapLocation.getErrorInfo());
                            //"服务端网络定位失败，可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com，会有人追查原因"
                        }
                    }
                    MyLocation savedLocation = new MyLocation();
                    savedLocation.coorType = MyLocation.COORTYPE_GCJ02;
                    savedLocation.locType = aMapLocation.getLocationType();//BDLocation.TypeGpsLocation BDLocation.TypeNetWorkLocation
                    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Date date = new Date(aMapLocation.getTime());
                    savedLocation.time = df.format(date);//定位时间
                    savedLocation.timeD = date;//定位时间
                    savedLocation.lon = aMapLocation.getLongitude(); //获取经度信息
                    savedLocation.lat = aMapLocation.getLatitude(); //获取纬度信息
                    savedLocation.direction = aMapLocation.getBearing();//方向信息
                    savedLocation.acc = aMapLocation.getAccuracy(); //获取定位精准度 获取海拔高度信息，单位米
                    savedLocation.add = aMapLocation.getAddress();//获取地址信息
                    savedLocation.speed = aMapLocation.getSpeed();
                    //city = aMapLocation.getCity() == null ? null : aMapLocation.getCity().replace("市", "");
                    savedLocation.city = aMapLocation.getCity();
                    savedLocation.district = aMapLocation.getDistrict();
                    savedLocation.setOrgLoc(aMapLocation);
                    return savedLocation;
                }
            }).subscribeOn(Schedulers.io()).observeOn(Schedulers.io());
        }
    }

    @Override
    public RxLocationManager setOption(final ClientOption option) {
        if (option == null) {
            throw new IllegalArgumentException("Option can't be null.");
        }
        clientOption = option;
        mAMapLocationClient.setLocationOption(option.getAmapOption());
        return this;
    }

    @SuppressWarnings("PMD.NullAssignment")
    @Override
    public void shutDown() {
        synchronized (AMapRxLocationManager.class) {
            if (mAMapLocationClient != null) {
                if (mAMapLocationClient.isStarted()) {
                    mAMapLocationClient.stopLocation();
                }
                mAMapLocationClient.onDestroy();
            }
            mAMapLocationClient = null;
//            sINSTANCE = null;
        }
    }

    static class RxLocationListener implements ObservableSource<AMapLocation> {
        private final AMapLocationClient mAMapLocationClient;
        private AMapLocationListener mListener;

        RxLocationListener(final AMapLocationClient client) {
            mAMapLocationClient = client;
        }

        @Override
        public void subscribe(final Observer<? super AMapLocation> observer) {
            mListener = new AMapLocationListener() {
                @Override
                public void onLocationChanged(final AMapLocation aMapLocation) {
                    if (mAMapLocationClient.isStarted()) {
                        observer.onNext(aMapLocation);
                    }
                }
            };
            mAMapLocationClient.setLocationListener(mListener);
            synchronized (RxLocationListener.class) {
                mAMapLocationClient.startLocation();
            }
            observer.onSubscribe(new Disposable() {

                @Override
                public void dispose() {
//                    if (!observer.isUnsubscribed()) {
//                        observer.unsubscribe();
//                    }
                    removeListener();
                }

                @Override
                public boolean isDisposed() {
                    return !mAMapLocationClient.isStarted();
//                    return observer.isUnsubscribed();
                }
            });
        }

        private void removeListener() {
            if (mListener != null) {
                mAMapLocationClient.unRegisterLocationListener(mListener);
                synchronized (RxLocationListener.class) {
                    if (mAMapLocationClient.isStarted()) {
                        mAMapLocationClient.stopLocation();
                    }
                    mAMapLocationClient.onDestroy();
                }
            }
        }
    }
}
